﻿// -------------------------------------------------------------------------
//    @FileName         :    NFPackagePart.cpp
//    @Author           :    gaoyi
//    @Date             :    24-8-29
//    @Email            :    445267987@qq.com
//    @Module           :    NFPackagePart
//
// -------------------------------------------------------------------------

// --- 文件总结 ---------------------------------------------------------------
// 本文件实现 NFPackagePart 类的功能：
// - 构造/析构：根据创建模式调用 CreateInit 或 ResumeInit
// - CreateInit/ResumeInit：初始化背包部件
// - LoadFromDB：从数据库加载所有背包页签数据
// - InitConfig：初始化配置（设置初始容量、购买次数等）
// - SaveDB：保存所有背包页签数据到数据库
// - OnRegister：注册时初始化金钱和物品使用限制
// - MakePlayerDataCS：构造客户端数据（所有背包页签和金钱）
// - MakeCityPlayerData：构造城市玩家数据
// - SaveRoleSimpleInfo：保存角色简要信息
// - GetPageByType：根据页签类型获取对应的背包页签
// - IsFull：检查指定物品的背包是否已满
// - Arrage：整理指定背包页签
// - GetCapacity/GetEmptyGridNum：获取容量和空格子数
// - GetItem/GetItemNum/GetItemByUUID/GetItemByItemId：获取物品相关信息
// - ExpandCapacity/ShrinkCapacity：扩展/缩减背包容量
// - GetMoney/GetMoneyNumber/SetMoneyNumber：金钱相关操作
// - GetItemUseLimite/CheckUseLimite/OnUse：物品使用限制相关操作
// - IsTempStorageHasItem：检查临时存储是否有物品
// ---------------------------------------------------------------------------

#include "NFPackagePart.h"
#include "NFPlayer.h"
#include <ConstantDescEx.h>
#include <NFEventDisp.h>
#include <NFPackageUtil.h>
#include <NFPlayerMgr.h>
#include <NFServicePackage.h>
#include <DescStore/RoleInitInfoDesc.h>
#include <NFLogicCommon/NFItemCreator.h>

#include "proto_db.pb.h"

NFPackagePart::NFPackagePart()
{
    if (EN_OBJ_MODE_INIT == NFShmMgr::Instance()->GetCreateMode())
    {
        CreateInit();
    }
    else
    {
        ResumeInit();
    }
}

NFPackagePart::~NFPackagePart()
{
}

int NFPackagePart::CreateInit()
{
    m_bIsInited = false;
    m_roleId = 0;
    return 0;
}

int NFPackagePart::ResumeInit()
{
    return 0;
}

int NFPackagePart::LoadFromDB(const proto_ff::tbRoleDetail* pDBData)
{
    const proto_ff::RoleItemData& dbItemData = pDBData->role_fortune_data().item();

    m_stPageEquip.Init(m_roleId, EN_BPT_EQUIP, dbItemData.grid_list_equip());
    m_stPageMatrial.Init(m_roleId, EN_BPT_MATRIAL, dbItemData.grid_list_matrial());
    m_stPageProp.Init(m_roleId, EN_BPT_PROP, dbItemData.grid_list_prop());
    m_stPageSkin.Init(m_roleId, EN_BPT_SKIN, dbItemData.grid_list_skin());
    m_stPageTitle.Init(m_roleId, EN_BPT_TITLE, dbItemData.grid_list_title());
    m_stPageGem.Init(m_roleId, EN_BPT_GEM, dbItemData.grid_list_gem());
    m_stPagePartnerEquip.Init(m_roleId, EN_BPT_PARTNER_EQUIP, dbItemData.grid_list_partner_equip());
    m_stPagePartnerEquipStone.Init(m_roleId, EN_BPT_PARTNER_EQUIP_STONE, dbItemData.grid_list_partner_equip_stone());
    m_stPagePartnerToken.Init(m_roleId, EN_BPT_PARTNER_TOKEN, dbItemData.grid_list_partner_token());
    m_stPageBuffEquip.Init(m_roleId, EN_BPT_BUFF_EQUIP, dbItemData.grid_list_buff_equip());
    m_stPageDressedItems.Init(m_roleId, EN_BPT_DRESSED_ITEM, dbItemData.grid_list_dressed());
    m_stPageTempStorage.Init(m_roleId, EN_BPT_TEMP_STORAGE, dbItemData.grid_list_temp());
    m_bIsInited = true;
    return 0;
}

int NFPackagePart::InitConfig(proto_ff::tbRoleDetail* pDbData, const RoleOne& stRoleOne)
{
    auto pItemData = pDbData->mutable_role_fortune_data()->mutable_item();
    CHECK_NULL(0, pItemData);

    pItemData->mutable_grid_list_equip()->set_max_grids(
        ConstantDescEx::Instance()->GetValue(proto_ff::EN_CONST_BAG_GRID_NUM_OF_BRON));
    pItemData->mutable_grid_list_equip()->set_buy_grids_times(0);

    pItemData->mutable_grid_list_title()->set_max_grids(
        ConstantDescEx::Instance()->GetValue(proto_ff::EN_CONST_TITLE_GRID_NUM_OF_BRON));
    pItemData->mutable_grid_list_title()->set_buy_grids_times(0);

    pItemData->mutable_grid_list_matrial()->set_max_grids(
        ConstantDescEx::Instance()->GetValue(proto_ff::EN_CONST_BAG_GRID_NUM_OF_BRON));
    pItemData->mutable_grid_list_matrial()->set_buy_grids_times(0);

    pItemData->mutable_grid_list_prop()->set_max_grids(
        ConstantDescEx::Instance()->GetValue(proto_ff::EN_CONST_BAG_GRID_NUM_OF_BRON));
    pItemData->mutable_grid_list_prop()->set_buy_grids_times(0);

    pItemData->mutable_grid_list_skin()->set_max_grids(
        ConstantDescEx::Instance()->GetValue(proto_ff::EN_CONST_FASHION_INIT_SLOT));
    pItemData->mutable_grid_list_skin()->set_buy_grids_times(0);

    pItemData->mutable_grid_list_gem()->set_max_grids(
        ConstantDescEx::Instance()->GetValue(proto_ff::EN_CONST_GEM_GRID_NUM_OF_BRON));
    pItemData->mutable_grid_list_gem()->set_buy_grids_times(0);

    pItemData->mutable_grid_list_partner_equip()->set_max_grids(
        ConstantDescEx::Instance()->GetValue(proto_ff::EN_CONST_PARTNER_EQUIP_GRID_NUM_OF_BRON));
    pItemData->mutable_grid_list_partner_equip()->set_buy_grids_times(0);

    pItemData->mutable_grid_list_partner_equip_stone()->set_max_grids(
        ConstantDescEx::Instance()->GetValue(proto_ff::EN_CONST_PARTNER_EQUIP_GRID_NUM_OF_BRON));
    pItemData->mutable_grid_list_partner_equip_stone()->set_buy_grids_times(0);

    pItemData->mutable_grid_list_partner_token()->set_max_grids(
        ConstantDescEx::Instance()->GetValue(proto_ff::EN_CONST_PARTNER_TOKEN_GRID_NUM_OF_BRON));
    pItemData->mutable_grid_list_partner_token()->set_buy_grids_times(0);

    pItemData->mutable_grid_list_dressed()->set_max_grids(MAX_DRESSED_ITEM);
    pItemData->mutable_grid_list_dressed()->set_buy_grids_times(0);

    pItemData->mutable_grid_list_temp()->set_max_grids(MAX_TEMP_STORAGE_SIZE);
    pItemData->mutable_grid_list_temp()->set_buy_grids_times(0);

    return 0;
}

int NFPackagePart::SaveDB(proto_ff::tbRoleDetail* pDBData)
{
    auto pItemData = pDBData->mutable_role_fortune_data()->mutable_item();
    m_stPageEquip.MakeCSData(pItemData->mutable_grid_list_equip());
    m_stPageMatrial.MakeCSData(pItemData->mutable_grid_list_matrial());
    m_stPageProp.MakeCSData(pItemData->mutable_grid_list_prop());
    m_stPageSkin.MakeCSData(pItemData->mutable_grid_list_skin());
    m_stPageTitle.MakeCSData(pItemData->mutable_grid_list_title());
    m_stPageGem.MakeCSData(pItemData->mutable_grid_list_gem());
    m_stPagePartnerEquip.MakeCSData(pItemData->mutable_grid_list_partner_equip());
    m_stPagePartnerEquipStone.MakeCSData(pItemData->mutable_grid_list_partner_equip_stone());
    m_stPagePartnerToken.MakeCSData(pItemData->mutable_grid_list_partner_token());
    m_stPageBuffEquip.MakeCSData(pItemData->mutable_grid_list_buff_equip());
    m_stPageDressedItems.MakeCSData(pItemData->mutable_grid_list_dressed());
    m_stPageTempStorage.MakeCSData(pItemData->mutable_grid_list_temp());
    return 0;
}

NFBagPage* NFPackagePart::GetPageByType(int iPageType)
{
    switch (iPageType)
    {
        case EN_BPT_EQUIP: return &m_stPageEquip;
        case EN_BPT_MATRIAL: return &m_stPageMatrial;
        case EN_BPT_PROP: return &m_stPageProp;
        case EN_BPT_GEM: return &m_stPageGem;
        case EN_BPT_SKIN: return &m_stPageSkin;
        case EN_BPT_TITLE: return &m_stPageTitle;
        case EN_BPT_PARTNER_EQUIP: return &m_stPagePartnerEquip;
        case EN_BPT_PARTNER_EQUIP_STONE: return &m_stPagePartnerEquipStone;
        case EN_BPT_PARTNER_TOKEN: return &m_stPagePartnerToken;
        case EN_BPT_BUFF_EQUIP: return &m_stPageBuffEquip;
        case EN_BPT_TEMP_STORAGE: return &m_stPageTempStorage;
        case EN_BPT_DRESSED_ITEM: return &m_stPageDressedItems;
        default:
            LOGSVR_ERROR("can not find page type: " << iPageType);
            return nullptr;
    }
    return nullptr;
}

const NFBagPage* NFPackagePart::GetPageByType(int iPageType) const
{
    return const_cast<NFPackagePart*>(this)->GetPageByType(iPageType);
}

bool NFPackagePart::IsFull(uint32_t iItemID)
{
    auto pItemDesc = ItemDesc::Instance()->GetDesc(iItemID);
    CHECK_EXPR(pItemDesc, true, "item id error:{}", iItemID);

    if (pItemDesc->item_type == EN_ITEM_TYPE_COIN)
    {
        return false;
    }

    auto pPage = GetPageByType(pItemDesc->bag_page_type);
    if (!pPage)
    {
        return true;
    }

    return pPage->GetEmptyGridNum();
}

NFBagPage* NFPackagePart::Arrage(int iPage)
{
    NFBagPage* pPage = GetPageByType(iPage);
    CHECK_EXPR(pPage, nullptr, "can not find page type: {}", iPage);

    pPage->Sort();

    return pPage;
}

ItemUseLimite* NFPackagePart::GetItemUseLimite(int iItemId)
{
    auto iter = m_astItemUseLimit.find(iItemId);
    if (iter != m_astItemUseLimit.end())
    {
        return &iter->second;
    }
    return nullptr;
}

int NFPackagePart::CheckUseLimite(int iItemId, int iNum)
{
    auto pstItemDesc = ItemDesc::Instance()->GetDesc(iItemId);
    CHECK_NULL(m_roleId, pstItemDesc);

    auto pstItemUseLimite = GetItemUseLimite(iItemId);
    if (pstItemUseLimite)
    {
        if (pstItemDesc->day_use_limite > 0 && pstItemUseLimite->day_use_count + iNum > pstItemDesc->day_use_limite)
        {
            return proto_ff::ERR_CODE_ITEM_DAY_USE_LIMITED;
        }

        if (pstItemDesc->week_use_limite > 0 && pstItemUseLimite->week_use_count + iNum > pstItemDesc->week_use_limite)
        {
            return proto_ff::ERR_CODE_ITEM_WEEK_USE_LIMITED;
        }
    }
    else
    {
        if (pstItemDesc->day_use_limite > 0 || pstItemDesc->week_use_limite > 0)
        {
            if (IsUseLimiteFull())
            {
                return proto_ff::ERR_CODE_ITEM_USE_LIMITE_FULL;
            }

            if (pstItemDesc->day_use_limite > 0 && iNum > pstItemDesc->day_use_limite)
            {
                return proto_ff::ERR_CODE_ITEM_DAY_USE_LIMITED;
            }

            if (pstItemDesc->week_use_limite > 0 && iNum > pstItemDesc->week_use_limite)
            {
                return proto_ff::ERR_CODE_ITEM_WEEK_USE_LIMITED;
            }
        }
    }

    return 0;
}

int NFPackagePart::OnUse(int iItemId, int iNum)
{
    auto pstItemUseLimite = GetItemUseLimite(iItemId);
    if (pstItemUseLimite)
    {
        pstItemUseLimite->day_use_count += iNum;
        pstItemUseLimite->week_use_count += iNum;
        pstItemUseLimite->last_use_time = NF_ADJUST_TIMENOW();

        LOGSVR_TRACE("ItemId:" << iItemId << ", cur use:" << pstItemUseLimite->day_use_count);
    }
    else
    {
        auto pstItemDesc = ItemDesc::Instance()->GetDesc(iItemId);
        CHECK_NULL(m_roleId, pstItemDesc);

        if (pstItemDesc->day_use_limite > 0 || pstItemDesc->week_use_limite > 0)
        {
            if (!IsUseLimiteFull())
            {
                pstItemUseLimite = &m_astItemUseLimit[iItemId];
                pstItemUseLimite->item_id = iItemId;
                pstItemUseLimite->day_use_count = iNum;
                pstItemUseLimite->week_use_count = iNum;
                pstItemUseLimite->last_use_time = NF_ADJUST_TIMENOW();
            }
            else
            {
                LOGSVR_ERROR("UseLimit Full");
            }
        }
    }

    return 0;
}

bool NFPackagePart::IsTempStorageHasItem()
{
    NFGridItem stItem;
    int iRetCode = m_stPageTempStorage.GetItem(&stItem, 0);
    if (iRetCode != 0)
    {
        return true;
    }
    else
    {
        return stItem.IsEmpty();
    }
}

Money* NFPackagePart::GetMoney(int iItemID)
{
    CHECK_EXPR(NFItemUtil::IsValidMoney(iItemID), NULL, "iItemID:{} is not valid", iItemID);
    auto iter = m_astMoneyMap.find(iItemID);
    if (iter != m_astMoneyMap.end())
    {
        return &iter->second;
    }

    if (m_astMoneyMap.size() >= m_astMoneyMap.max_size())
    {
        CHECK_ERR_RE_NULL(m_roleId, -1, "m_astMoneyMap mem space not enough, iItemID:{}", iItemID);
    }

    auto pItem = &m_astMoneyMap[iItemID];
    pItem->id = iItemID;
    pItem->value = 0;
    return pItem;
}

int NFPackagePart::SetMoneyNumber(int iItemID, int iNum)
{
    auto pItem = GetMoney(iItemID);
    CHECK_NULL(m_roleId, pItem);

    pItem->value = iNum;

    return 0;
}

uint64_t NFPackagePart::GetMoneyNumber(int iItemID)
{
    auto pItem = GetMoney(iItemID);
    CHECK_EXPR(pItem, 0, "iItemID:{}", iItemID);
    return pItem->value;
}

int NFPackagePart::GetCapacity(int iBagType) const
{
    auto pBagPage = GetPageByType(iBagType);
    CHECK_NULL_WF(m_roleId, pBagPage, "iBagType:{}", iBagType);

    return pBagPage->GetCapacity();
}

int NFPackagePart::GetEmptyGridNum(int iBagType) const
{
    auto pBagPage = GetPageByType(iBagType);
    CHECK_NULL_WF(m_roleId, pBagPage, "iBagType:{}", iBagType);

    return pBagPage->GetEmptyGridNum();
}

int NFPackagePart::GetItem(int iBagType, int iGridIndex, NFGridItem* pItemOut) const
{
    auto pBagPage = GetPageByType(iBagType);
    CHECK_NULL_WF(m_roleId, pBagPage, "iBagType:{}", iBagType);

    return pBagPage->GetItem(pItemOut, iGridIndex);
}

int NFPackagePart::GetItem(int iBagType, std::vector<NFGridItem>& vecItem) const
{
    auto pBagPage = GetPageByType(iBagType);
    CHECK_NULL_WF(m_roleId, pBagPage, "iBagType:{}", iBagType);

    return pBagPage->GetAllItemOut(vecItem);
}

int NFPackagePart::GetItemNum(int iBagType, int iItemId) const
{
    auto pBagPage = GetPageByType(iBagType);
    CHECK_NULL_WF(m_roleId, pBagPage, "iBagType:{}", iBagType);

    return pBagPage->GetItemNum(iItemId);
}

int NFPackagePart::GetItemByUUID(int iBagType, uint64_t ullUUID, NFGridItem* pItemOut) const
{
    auto pBagPage = GetPageByType(iBagType);
    CHECK_NULL_WF(m_roleId, pBagPage, "iBagType:{}", iBagType);

    return pBagPage->GetItemByUUID(pItemOut, ullUUID);
}

const NFGridItem* NFPackagePart::GetItemByUUID(int iBagType, uint64_t ullUUID) const
{
    auto pBagPage = GetPageByType(iBagType);
    CHECK_NULL_RE_NULL(m_roleId, pBagPage, "iBagType:{}", iBagType);
    return pBagPage->GetItemByUUID(ullUUID);
}

NFGridItem* NFPackagePart::GetItemByUUID(int iBagType, uint64_t ullUUID)
{
    auto pBagPage = GetPageByType(iBagType);
    CHECK_NULL_RE_NULL(m_roleId, pBagPage, "iBagType:{}", iBagType);
    return pBagPage->GetItemByUUID(ullUUID);
}

int NFPackagePart::GetItemByItemId(int iBagType, int iItemID, NFGridItem* pItemOut) const
{
    CHECK_NULL(0, pItemOut);
    auto pBagPage = GetPageByType(iBagType);
    CHECK_NULL_WF(m_roleId, pBagPage, "iBagType:{}", iBagType);

    for (int i = 0; i < pBagPage->GetCapacity(); i++)
    {
        auto pItem = pBagPage->GetItemByIndex(i);
        if (pItem && pItem->GetItemID() == iItemID)
        {
            *pItemOut = *pItem;
            return 0;
        }
    }
    return -1;
}

int NFPackagePart::ExpandCapacity(int iBagType, int iIncreaseCapacityNum)
{
    auto pBagPage = GetPageByType(iBagType);
    CHECK_NULL_WF(m_roleId, pBagPage, "iBagType:{}", iBagType);

    int iOrgCapacity = 0;
    int iNewCapacity = 0;

    int iRetCode = pBagPage->AddCapacity(iIncreaseCapacityNum, &iOrgCapacity, &iNewCapacity);
    CHECK_ERR(0, iRetCode, "AddCapacity Failed, iBagType:{} iIncreaseCapacityNum:{}", iBagType, iIncreaseCapacityNum);

    MarkDirty();

    LOG_DEBUG(0, "ExpandCapacity, iBagType:{} iIncreaseCapacityNum:{} iOrgCapacity:{} iNewCapacity:{}", iBagType, iIncreaseCapacityNum, iOrgCapacity, iNewCapacity);
    return 0;
}

int NFPackagePart::ShrinkCapacity(int iBagType, int iDecreaseCapacityNum)
{
    auto pBagPage = GetPageByType(iBagType);
    CHECK_NULL_WF(m_roleId, pBagPage, "iBagType:{}", iBagType);

    int iOrgCapacity = 0;
    int iNewCapacity = 0;

    int iRetCode = pBagPage->DecCapacity(iDecreaseCapacityNum, &iOrgCapacity, &iNewCapacity);
    CHECK_ERR(0, iRetCode, "DecCapacity Failed, iBagType:{} iDecreaseCapacityNum:{}", iBagType, iDecreaseCapacityNum);

    MarkDirty();

    LOG_DEBUG(0, "ExpandCapacity, iBagType:{} iDecreaseCapacityNum:{} iOrgCapacity:{} iNewCapacity:{}", iBagType, iDecreaseCapacityNum, iOrgCapacity, iNewCapacity);
    return 0;
}

int NFPackagePart::MakePlayerDataCS(proto_ff::Proto_CSGetRoleRsp* pCSGetRoleRsp)
{
    CHECK_NULL(0, pCSGetRoleRsp);
    CHECK_EXPR(m_bIsInited, -1, "data not inited, ");

    auto pItemDataRsp = pCSGetRoleRsp->mutable_item_data();

    for (auto iter = m_astMoneyMap.begin(); iter != m_astMoneyMap.end(); ++iter)
    {
        Money& money = iter->second;
        auto pItem = pItemDataRsp->add_other_list();
        pItem->set_id(money.id);
        pItem->set_value(money.value);
    }

    m_stPageEquip.MakeCSData(pItemDataRsp->mutable_grid_list_equip());
    m_stPageMatrial.MakeCSData(pItemDataRsp->mutable_grid_list_matrial());
    m_stPageProp.MakeCSData(pItemDataRsp->mutable_grid_list_prop());
    m_stPageSkin.MakeCSData(pItemDataRsp->mutable_grid_list_skin());
    m_stPageTitle.MakeCSData(pItemDataRsp->mutable_grid_list_title());
    m_stPageGem.MakeCSData(pItemDataRsp->mutable_grid_list_gem());
    m_stPagePartnerEquip.MakeCSData(pItemDataRsp->mutable_grid_list_partner_equip());
    m_stPagePartnerEquipStone.MakeCSData(pItemDataRsp->mutable_grid_list_partner_equip_stone());
    m_stPagePartnerToken.MakeCSData(pItemDataRsp->mutable_grid_list_partner_token());
    m_stPageBuffEquip.MakeCSData(pItemDataRsp->mutable_grid_list_buff_equip());

    for (int i = 0; i < (int)m_stPageDressedItems.GetCapacity(); ++i)
    {
        auto pItem = m_stPageDressedItems.GetItemByIndex(i);
        if (pItem && !pItem->IsEmpty())
        {
            NFItemUtil::ItemToCSData(*pItem, *pItemDataRsp->add_dressed_items(), i);
        }
    }

    m_stPageTempStorage.Sort();
    for (int i = 0; i < (int)m_stPageTempStorage.GetCapacity(); ++i)
    {
        auto pItem = m_stPageTempStorage.GetItemByIndex(i);
        if (pItem)
        {
            if (pItem->IsEmpty()) break;
            NFItemUtil::ItemToCSData(*pItem, *pItemDataRsp->add_temp_storage(), i);
        }
    }

    for (auto iter = m_astItemUseLimit.begin(); iter != m_astItemUseLimit.end(); ++iter)
    {
        iter->second.ToPb(pItemDataRsp->add_item_limite());
    }
    return 0;
}

int NFPackagePart::MakeCityPlayerData(proto_ff::CityRoleInfo* pGCCityRoleInfo)
{
    return 0;
}

int NFPackagePart::SaveRoleSimpleInfo(proto_ff::Proto_RoleSimpleInfo* pDbData)
{
    pDbData->set_role_tax(GetMoneyNumber(proto_ff::EN_ID_CONTRIBUTION));
    return 0;
}

int NFPackagePart::OnRegister()
{
    std::vector<Item> vecItems;
    for (int i = 0; i < (int)RoleInitInfoDesc::Instance()->GetResNum(); ++i)
    {
        auto pRoleInitInfo = RoleInitInfoDesc::Instance()->GetDescByIndex(i);
        CHECK_NULL(0, pRoleInitInfo);

        if (pRoleInitInfo->type == proto_ff::EN_ROLE_INIT_TYPE_ITEM)
        {
            Item item;
            item.id = pRoleInitInfo->ext_id;
            item.value = pRoleInitInfo->num;
            vecItems.push_back(item);
        }
    }

    int iRetCode = NFServicePackage::AddItems(GetPlayer(), vecItems, EN_ICR_CREATE, false, nullptr, false);
    CHECK_ERR(GetPlayer()->GetRoleID(), iRetCode, "AddItems failed");

    return 0;
}
